﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Library.Network {
  public class ServerListener : IDisposable {

    public ClientConnectedHash ClientConnectedHash { get; private set; }
    private long _clientNumber;
    private TcpListener _server;
    private bool _isStop;

    public void Open(IPEndPoint endPoint) {
      ClientConnectedHash = new ClientConnectedHash();
      _server = new TcpListener(endPoint);
      _server.Start();
    }

    public void Close() {
      Dispose();
    }

    public void DoAcept() {
      while (!_isStop) {
        TcpClient tcpClient;
        try {
          tcpClient = _server.AcceptTcpClient();
          InvokeSocketAccepted(new SocketAcceptedEventArgs() { Client = tcpClient });
        } catch (SocketException) {
          _isStop = true;
          return;
        } catch (InvalidOperationException) {
          _isStop = true;
          return;
        }


        _clientNumber++;
        var e = new ClientConnectedEventArgs { Client = new Connector { ConnectNumnber = _clientNumber, ClientSocket = tcpClient }, ProcessInNewThread = true };
        InvokeClientConnected(e);
        if (e.Handler != null) {
          e.Handler.Server = this;
          ClientConnectedHash.AddConnector(e.Client);
          e.Handler.Client = e.Client;
          if (e.ProcessInNewThread) {
            var thread = new Thread(e.Handler.DoReceive);
            e.Client.ProcessingThread = thread;
            thread.Start();
          } else {
            e.Handler.DoReceive();
          }
        }
      }
    }



    public event EventHandler<ClientConnectedEventArgs> ClientConnected;
    public event EventHandler<ClientDisconnectedEventArgs> ClientDiconnected;
    public event EventHandler<SocketAcceptedEventArgs> SocketAccepted;
    internal void InvokeSocketAccepted(SocketAcceptedEventArgs e) {
      if (_isStop) return;

      EventHandler<SocketAcceptedEventArgs> handler = SocketAccepted;
      if (handler != null) handler(this, e);
    }
    internal void InvokeClientConnected(ClientConnectedEventArgs e) {
      if (_isStop) return;
      e.Client.IsConnected = true;
      EventHandler<ClientConnectedEventArgs> handler = ClientConnected;
      if (handler != null) handler(this, e);
    }
    internal void InvokeClientDiconnected(ClientDisconnectedEventArgs e) {
      if (_isStop) return;
      e.Client.IsConnected = false;
      ClientConnectedHash.Remove(e.Client.ConnectNumnber);
      EventHandler<ClientDisconnectedEventArgs> handler = ClientDiconnected;
      if (handler != null) handler(this, e);
    }

    public void Dispose() {
      _isStop = true;
      if (_server != null) {
        try {
          _server.Stop();
          foreach (var client in ClientConnectedHash) {
            var connector = client.Value;
            if (connector == null) continue;

            if (connector.IsConnected) {
              try {
                if (connector.ProcessingThread != null)
                  if (connector.ProcessingThread.ThreadState == ThreadState.Running || connector.ProcessingThread.ThreadState == ThreadState.Background)
                    connector.ProcessingThread.Abort();
              } catch {
              }
            }
          }
        } catch {
        }
      }
    }
  }
}
